﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

using RaisingStudio.SmallProgram.Library;

namespace RaisingStudio.SmallProgram.Library.Internal
{
    public static class SmallProgramApplication
    {
        private static Application _application;
        private static Thread _applicationThread;
        private static System.Windows.Threading.Dispatcher _dispatcher;
        private static int _pendingOperations = 0;
        internal static Queue<InvokeHelper> mainThreadActions = new Queue<InvokeHelper>();

        static SmallProgramApplication()
        {
            AutoResetEvent autoEvent = new AutoResetEvent(false);
            _applicationThread = new Thread(delegate()
            {
                Application application = new Application();
                autoEvent.Set();
                _application = application;
                application.Run();
            });
            _applicationThread.SetApartmentState(ApartmentState.STA);
            _applicationThread.Start();
            autoEvent.WaitOne();
            _dispatcher = System.Windows.Threading.Dispatcher.FromThread(_applicationThread);
        }

        internal static void BeginInvoke(InvokeHelper invokeDelegate)
        {
            _pendingOperations++;
            _dispatcher.BeginInvoke(DispatcherPriority.Render, invokeDelegate);
            if (_pendingOperations > 40)
            {
                ClearDispatcherQueue();
                _pendingOperations = 0;
            }
        }

        public static void BeginProgram()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(SmallProgramApplication.HandleException);
        }

        internal static void ClearDispatcherQueue()
        {
            try
            {
                // TODO: make sure, how to ClearDispatcherQueue?!
                //_dispatcher.Invoke(DispatcherPriority.Background, null, null);
                _dispatcher.Invoke(DispatcherPriority.Background, new Func<object, object>(delegate
                {
                    return null;
                }), null);
            }
            catch
            {
            }
        }

        internal static BitmapSource CropBitmap(BitmapSource original, Primitive left, Primitive top, Primitive width, Primitive height)
        {
            if (left < 0)
            {
                left = 0;
            }
            if (top < 0)
            {
                top = 0;
            }
            if ((left + width) > original.PixelWidth)
            {
                width = original.PixelWidth - left;
            }
            if ((top + height) > original.PixelHeight)
            {
                height = original.PixelHeight - top;
            }
            return (BitmapSource)InvokeWithReturn(() => new CroppedBitmap(original, new Int32Rect(left, top, width, height)));
        }

        internal static void End()
        {
            Invoke(delegate
            {
                _application.Shutdown();
                _dispatcher.InvokeShutdown();
            });
            if (AppDomain.CurrentDomain.FriendlyName != "Debuggee")
            {
                Process.GetCurrentProcess().Kill();
            }
        }

        public static void EndProgram()
        {
            if (!GraphicsWindow._windowVisible && Timer.Interval == 100000000)
            {
                Invoke(delegate
                {
                    _application.Shutdown();
                    _dispatcher.InvokeShutdown();
                });
                if (AppDomain.CurrentDomain.FriendlyName != "Debuggee")
                {
                    Process.GetCurrentProcess().Kill();
                }
            }
        }

        internal static string GetEntryAssemblyPath()
        {
            return Assembly.GetEntryAssembly().Location;
        }

        internal static Uri GetResourceUri(string resourceName)
        {
            return new Uri(string.Format("pack://application:,,/RaisingStudio.SmallProgram.Library;component/Resources/{0}", resourceName));
        }

        private static void HandleException(object sender, UnhandledExceptionEventArgs e)
        {
            Exception exception = e.ExceptionObject as Exception;
            if (exception != null)
            {
                Invoke(delegate
                {
                    Window window = new Window
                    {
                        Title = "Error in Small Program",
                        SizeToContent = SizeToContent.WidthAndHeight,
                        WindowStyle = WindowStyle.SingleBorderWindow,
                        ShowInTaskbar = false,
                        ResizeMode = ResizeMode.NoResize,
                        Topmost = true,
                        WindowStartupLocation = WindowStartupLocation.CenterScreen,
                        Content = new StackPanel
                        {
                            Margin = new Thickness(4.0),
                            Children = 
							    {
								    new DockPanel
								    {
									    Children = 
									    {
										    new Image
										    {
											    Source = new BitmapImage(new Uri("pack://application:,,/RaisingStudio.SmallProgram.Library;component/Resources/Error.png")), 
											    Width = 48.0, 
											    Height = 48.0, 
											    Margin = new Thickness(8.0), 
											    VerticalAlignment = VerticalAlignment.Top
										    }, 
										    new StackPanel
										    {
											    Children = 
											    {
												    new TextBlock
												    {
													    Text = exception.Message, 
													    Margin = new Thickness(4.0), 
													    FontSize = 16.0, 
													    Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 42, 181))
												    }, 
												    new TextBox
												    {
													    Text = exception.StackTrace, 
													    FontFamily = new FontFamily("Consolas"), 
													    IsReadOnly = true, 
													    MaxWidth = 480.0, 
													    Margin = new Thickness(4.0), 
													    HorizontalScrollBarVisibility = ScrollBarVisibility.Auto, 
													    VerticalScrollBarVisibility = ScrollBarVisibility.Auto
												    }
											    }
										    }
									    }
								    }, 
								    new StackPanel
								    {
									    Orientation = Orientation.Horizontal, 
									    HorizontalAlignment = HorizontalAlignment.Right, 
									    Children = 
									    {
										    new Button
										    {
											    Content = "OK", 
											    Margin = new Thickness(4.0), 
											    Width = 60.0, 
											    IsCancel = true
										    }
									    }
								    }
							    }
                        }
                    };
                    window.ShowDialog();
                });
            }
        }

        internal static void Invoke(InvokeHelper invokeDelegate)
        {
            _dispatcher.Invoke(DispatcherPriority.Render, invokeDelegate);
        }

        internal static object InvokeWithReturn(InvokeHelperWithReturn invokeDelegate)
        {
            return _dispatcher.Invoke(DispatcherPriority.Render, invokeDelegate);
        }

        internal static System.Windows.Threading.Dispatcher Dispatcher
        {
            get
            {
                return _dispatcher;
            }
        }

        internal static bool HasShutdown
        {
            get
            {
                return Dispatcher.HasShutdownFinished;
            }
        }
    }
}

